Monitor (physical screen)
└─ Window frame (decorations)
└─ Window client area (GLFW window size)
└─ Framebuffer (pixel resolution)
└─ Swapchain images
└─ Viewport/scissor (render region)
Monitor size (physical display resolution)
monitor_get_size() -> { 2560, 1440 }
monitor_get_size :: proc(monitor_idx: int = 0) -> (extent: spatial.Extent) {
monitors := glfw.GetMonitors()
assert(monitor_idx < len(monitors))
video_mode := glfw.GetVideoMode(monitors[monitor_idx])
return { u32(video_mode.width), u32(video_mode.height) }
}
-
This is the full screen resolution of the monitor.
-
A window when maximized in the OS (not fullscreen) can be like:
monitor height: 1440 window height: 1377-
Difference:
1440 - 1377 = 63 px-
That space is taken by things like:
-
taskbar/dock
-
window decorations when maximized
-
OS reserved areas
-
-
Frame size (window decorations)
glfw.GetWindowFrameSize() -> { 8, 31, 8, 8 }
-
Order is:
-
(left, top, right, bottom)
-
-
These are the OS window borders, not part of the client area.
-
So your full outer window size is:
-
width = 2560 + 8 + 8 = 2576
-
height = 1377 + 31 + 8 = 1416
-
-
This is mostly useful for window positioning.
Window size (logical size)
glfw.GetWindowSize() -> { 1280, 720 }
-
This is the size of the client area in screen coordinates (logical pixels).
-
Excludes OS decorations (title bar, borders)
-
Used for UI/layout logic
-
May differ from framebuffer size on HiDPI displays
-
If the framebuffer size matches, you are effectively at 1.0 content scale.
Framebuffer size (pixel size)
glfw.GetFramebufferSize() -> { 1280, 720 }
-
This is the actual render target pixel resolution.
-
In Vulkan this is the important one because it determines:
-
swapchain image size
-
render pass attachments
-
viewport/scissor defaults
-
-
Fhe framebuffer is the storage the fragment shader writes into.
-
On HiDPI displays:
-
window size != framebuffer size
-
-
Example (Retina):
-
window: 1280×720
-
framebuffer: 2560×1440
-
Swapchain extent (Vulkan image size)
window.swapchain.extent -> { 1280, 720 }
-
This is the size of the images returned by the Vulkan swapchain.
-
It should normally equal the framebuffer size because:
-
swapchain extent == framebuffer size
-
-
unless you are doing something unusual.
-
This value is what your render pass ultimately writes into.
Viewport size (Vulkan dynamic state)
VkViewport viewport;
viewport.width = swapchainExtent.width;
viewport.height = swapchainExtent.height;
-
The viewport defines how framebuffer pixels map to NDC, not the size of the framebuffer itself.
-
Think of it as:
-
framebuffer size → storage
-
viewport → mapping region inside that storage
-
-
As the framebuffer is the storage the fragment shader writes into, the viewport is the mapping that determines where fragments land inside that storage.
-
You can render to only part of the swapchain image by shrinking the viewport.